#pragma once
#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>

// 线程信息
struct ThreadInfo
{
    pthread_t tid;
    std::string name;
};

template <class T>
class ThreadPool
{
    static const int default_num = 5;

public:
    // 封装加解锁操作
    void Lock()
    {
        pthread_mutex_lock(&mutex_);
    }

    void Unlock()
    {
        pthread_mutex_unlock(&mutex_);
    }

    void Wakeup()
    {
        // 唤醒线程 在指定的 条件变量下去等待
        pthread_cond_signal(&cond_);
    }

    void ThreadSleep()
    {
        // 当前线程如果没有申请到，会释放锁再去排队
        pthread_cond_wait(&cond_, &mutex_);
    }

    bool IsQueueEmpty()
    {
        return tasks_.empty();
    }

    std::string GetThreadName(pthread_t tid)
    {
        //遍历数组，看当前是哪一个线程
        for(const auto& e : threads_)
        {
            if(e.tid == tid)
                return e.name;
        }
        return "None";
    }

public:

    // 静态成员函数  不能访问 类内变量
    // static void *HandlerTask(void *args)
    // {
    //     while (true)
    //     {
    //         //每个进程先检测有没有任务，没有再去休眠
    //         Lock();
    //         while(tasks_.empty())  //while 而不是 if
    //         {
    //             //如果任务队列为空
    //             ThreadSleep();
    //         }
    //         T t =  tasks_.front();
    //         tasks_.pop();
    //         Unlock();   //在你解锁后  其他线程又可以继续去申请锁，提高并发度
    //         //任务运行可以不在锁内，交给线程运行
    //         t();
    //     }
    // }

    //   外部对象的指针 this  不能访问 私有方法 ！！！
    //******************************************************************************************************************************************//
    // 因为如果不是 static 类内的成员函数都有一个隐藏的 this 指针传递， 但是线程函数规定只能传一个值， 所以会报传参个数不匹配的错误！！！！！！***************************/
    static void *HandlerTask(void *args)
    {
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        std::string name = tp->GetThreadName(pthread_self());
        while (true)
        {
            // 每个进程先检测有没有任务，没有再去休眠
            tp->Lock();
            while (tp->IsQueueEmpty()) // while 而不是 if   //也可以直接写 tp->tasks_.empty()
            {
                // 如果任务队列为空
                tp->ThreadSleep();
            }
            T t = tp->Pop();
            tp->Unlock(); // 在你解锁后  其他线程又可以继续去申请锁，提高并发度
            // 任务运行可以不在锁内，交给线程运行
            t();

            // 这里也可以在写一个成员变量 函数 来接收返回值
            // 这里直接打印
            std::cout << name << " run, " << "result: " << t.GetResult() << std::endl;
        }
    }
    T Pop()
    {
        T t = tasks_.front();
        tasks_.pop();
        return t;
    }
    // 线程池启动  创建一批线程
    void Start()
    {
        int num = threads_.size();
        for (int i = 0; i < num; ++i)
        {
            threads_[i].name = "thread-" + std::to_string(i + 1);
            pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this); // 为了防止 static 函数不能访问类内函数，类内变量， 直接把对象传过去 this
            // pthread_create(&(threads_[i].tid), nullptr, HandlerTask, nullptr); // 输出型参数，返回线程ID
        }
    }

    // 外面向内push任务接口
    void Push(T &t)
    {
        // 进来就先Lock
        Lock();
        tasks_.push(t); // 队列进任务
        std::cout << "t oper is " << t.GetOper() << std::endl;
        Wakeup();
        Unlock();
    }

    //Instance 实例 例子  向外部提供接口函数
    // 只有静态函数能用静态变量 能直接访问类内静态变量
    // 这里也可以传参数
    // 如果多线程并发来访问  要加锁
    static ThreadPool<T>* GetInstance()
    {
        if(tp_ == nullptr)  //双重判断
        {
            pthread_mutex_lock(&lock);  //如果只是这里 多线程进来的时候只有一次能访问，但是后续每个线程进来都要进行加减锁
            if(tp_ == nullptr)
            {
                std::cout<<"singleton creat done first"<<std::endl;
                tp_ = new ThreadPool<T>(); //加不加括号都可以 如果有值可以初始化
            }
            pthread_mutex_unlock(&lock);
        }
        return tp_;
    }

private:
    //为了实现单例 拷贝 析构 以及有可能发生拷贝生产临时变量的函数 都要私有
    ThreadPool(int num = default_num)
        : threads_(num)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }

    //把拷贝构造 =赋值语句 给去掉
    //可以定义出来参数  不用也可以不定义 ThreadPool(const ThreadPool<T>& a) = delete;
    ThreadPool(const ThreadPool<T>&) =delete;
    const ThreadPool<T>& operator=(const ThreadPool<T>&) const =delete; //为了防止 a = b = c  赋值操作 在最前面加上const

private:
    std::vector<ThreadInfo> threads_; // 包含进程信息
    std::queue<T> tasks_;

    pthread_mutex_t mutex_; // 互斥锁
    pthread_cond_t cond_;   // 条件变量


    //线程池 如何改成懒汉单例模式
    //只有一个对象 所以要设置成static  所有的成员变量共享
    static ThreadPool<T> * tp_;
    static pthread_mutex_t lock_;
};

//类内定义 类外声明
template<class T>
ThreadPool<T>* ThreadPool<T>::tp_ = nullptr;

template<class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;
